package top.yexuejc.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;

/**
 * 请求处理
 *
 * @author maxiaofeng
 * @date 2025/6/19 11:29
 */
public class RequestHandler implements Runnable {
    Logger logger = Logger.getLogger(RequestHandler.class.getName());
    private final String OS_NAME = System.getProperty("os.name");
    private final List<String> STATIC_RESOURCES = List.of("css", "js", "img", "fonts", "favicon.ico");

    private final Socket clientSocket;
    private final String staticRoot;
    private final String webRoot;

    public RequestHandler(Socket socket, String staticRoot, String webRoot) {
        this.clientSocket = socket;
        this.staticRoot = staticRoot;
        this.webRoot = webRoot;
    }

    @Override
    public void run() {
        try (InputStream input = clientSocket.getInputStream(); OutputStream output = clientSocket.getOutputStream()) {

            // 解析请求
            Request request = parseRequest(input);

            // 处理请求并生成响应（静态画面）
            Response response = handleRequest(request);

            // 画面渲染（动态画面）
            pageLive(request, response);

            // 发送响应
            sendResponse(output, response);

        } catch (Exception e) {
            System.err.println("Error handling request: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                System.err.println("Error closing client socket: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    private void pageLive(Request request, Response response) {
        if (!request.getPath().endsWith("html")) {
            return;
        }
        byte[] originalBytes = response.getContent();
        // 模拟模板引擎
        // 1. 获取原始的字符串
        String originalString = new String(originalBytes, StandardCharsets.UTF_8);

        // 2. 替换字符串
        String replacedString = originalString.replace("${body}", request.getParams());

        // 3. 将替换后的 String 转回 byte[]
        response.setContent(replacedString.getBytes(StandardCharsets.UTF_8));
    }

    private Request parseRequest(InputStream input) throws IOException, URISyntaxException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        String requestLine = reader.readLine();
        if (requestLine == null) {
            throw new IOException("Empty request");
        }
        // GET /index.html?a=1 HTTP/1.1
        // Host: 127.0.0.1:8080
        // User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
        // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;
        // v=b3;q=0.7

        // POST /getUser HTTP/1.1
        // Content-Type: application/json
        // User-Agent: PostmanRuntime/7.44.0
        // Accept: */*
        // Host: 127.0.0.1:8080
        // Accept-Encoding: gzip, deflate, br
        // Connection: keep-alive
        // Content-Length: 10

        logger.info("=====================================================");
        logger.info(requestLine);
        String[] parts = requestLine.split(" ");
        if (parts.length != 3) {
            throw new IOException("Invalid request line: " + requestLine);
        }

        String method = parts[0]; // GET
        String path = parts[1]; // /index.html

        URI url = new URI(path);
        path = url.getPath();
        String params = url.getQuery();


        // 读取请求头
        String headerLine;
        int contentLength = 0; // 输入流中字节长度
        while ((headerLine = reader.readLine()) != null && !headerLine.isEmpty()) {
            logger.info(headerLine);
            if (method.equals("POST") && headerLine.startsWith("Content-Length:")) {
                contentLength = Integer.parseInt(headerLine.split(":")[1].trim());
            }
        }

        // 读取POST请求体
        StringBuilder requestBody = new StringBuilder();
        if (method.equals("POST") && contentLength > 0) {
            char[] buffer = new char[contentLength];
            reader.read(buffer, 0, contentLength);
            requestBody.append(buffer);
            logger.info("Body: " + requestBody);
        }
        logger.info("Params: " + params);

        logger.info("=====================================================");
        return new Request(method, path, requestBody.toString(), params);
    }

    private Response handleRequest(Request request) {
        String path = request.getPath();

        // 默认主页
        if (path.equals("/")) {
            path = "/index.html";
        }
        path = getAbsolutePath(path);

        // 获取classPath中的文件路径
        String classPath = Objects.requireNonNull(getClass().getClassLoader().getResource("")).getPath();
        if (OS_NAME.startsWith("Windows")) {
            classPath = classPath.substring(1);
        }
        Path filePath = Paths.get(classPath, path);

        // 安全检查，防止目录遍历攻击
        if (!filePath.startsWith(Paths.get(classPath, webRoot)) && !filePath.startsWith(Paths.get(classPath, staticRoot))) {
            return new Response(403, "Forbidden", "text/plain", "Access denied".getBytes());
        }

        // 检查文件是否存在且是普通文件
        if (Files.exists(filePath) && Files.isRegularFile(filePath)) {
            try {
                byte[] content = Files.readString(filePath, StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8);
                String contentType = determineContentType(filePath);
                return new Response(200, "OK", contentType, content);
            } catch (IOException e) {
                return new Response(500, "Internal Server Error", "text/plain", "Error reading file".getBytes());
            }
        } else {
            return new Response(404, "Not Found", "text/plain", "Page not found".getBytes());
        }
    }

    private String getAbsolutePath(String path) {
        if (path.startsWith("/") && STATIC_RESOURCES.stream().anyMatch(path::endsWith)) {
            return staticRoot + path;
        }
        return webRoot + path;
    }

    private String determineContentType(Path filePath) {
        String fileName = filePath.getFileName().toString();
        if (fileName.endsWith(".html")) {
            return "text/html";
        } else if (fileName.endsWith(".css")) {
            return "text/css";
        } else if (fileName.endsWith(".js")) {
            return "application/javascript";
        } else if (fileName.endsWith(".png")) {
            return "image/png";
        } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
            return "image/jpeg";
        } else {
            return "application/octet-stream";
        }
    }

    private void sendResponse(OutputStream output, Response response) throws IOException {
        String statusLine = STR."""
HTTP/1.1 \{response.getStatusCode()} \{response.getStatusMessage()}\r
""";
        String headers = STR."""
Content-Type: \{response.getContentType()}\r
Content-Length: \{response.getContentLength()}\r
\r
""";

        output.write(statusLine.getBytes());
        output.write(headers.getBytes());
        output.write(response.getContent());
        output.flush();
    }
}
